Add ⌘P worktree switcher (VS Code-style) with worktree MRU and direct-beats-fuzzy ranking#370
Add ⌘P worktree switcher (VS Code-style) with worktree MRU and direct-beats-fuzzy ranking#370COCPORN wants to merge 1 commit into
Conversation
Adds a project switcher on ⌘P and moves the existing Command Palette to ⌘⇧P, aligning with VS Code (⌘⇧P = Command Palette; ⌘P = fast navigation). Both shortcuts remain user-rebindable via shortcut overrides. - ⌘P opens a fuzzy switcher of repositories ordered most-recently-used. The current project is rendered (so you see where you are) but flagged `isCurrentProject` so the default selection lands on the *previous* project — ⌘P then Enter is a Cmd+Tab-style toggle. Typing snaps to the top fuzzy match. - Switching to a project resolves to the worktree you last had open there (`lastWorktreeByProject`), falling back to main/first. The MRU is recorded on BOTH navigation paths — `setSingleWorktreeSelection` (hotkeys/palette) and `reduceSelectionChangedEffect` (sidebar clicks) — so it works however you navigate. - New `PaletteMode` (.commands / .projectSwitcher), `selectProject` item/ delegate, and a `dismissedWithoutSelection` delegate so cancelling the palette refocuses the current terminal. - Command Palette hint text updated ⌘P → ⌘⇧P. Covered by CommandPaletteFeatureTests and RepositoriesFeatureProjectMRUTests (switcher ordering/flagging, defaultIndex seeding, sidebar-path MRU recording). Implemented with AI assistance (Claude Code); reviewed and verified in a real build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
I've made a quick video showcasing the changes. Let me know if you're interested, I'll turn it into two PRs, if not I'm happy to just run off this private fork going forward. The terminal-focus I actually consider a bug, I'd try to patch that even if you don't care for the project switcher. Thanks for considering it. Supacode.PR.Project.Swap.and.Terminal.Focus-web.mp4 |
|
Genuinely loving this ❤️ thanks |
|
So I've played a bit more with this, and I feel like the use case for this, exactly as implemented, relies too much on the assumption from the video that most "projects" only have one worktree. It becomes almost useless for agentic-driven multi-worktree scenarios, which tend to be the main use case for Supacode. I think the idea still has its merits, though, and I'd be more than happy to merge the two concerns, basically translating this into a palette for worktree selection history plus fuzzy search for everything not in history. Wdyt? @COCPORN |
|
I am not sure exactly what you mean, I don't really understand how the extra navigation palette interferes with worktrees. I'll probably just be running off my own fork going forward, because I actually need some other features that I'm fairly certain(?) won't be merged, so I am going to have to go off non mainline code personally anyway. Thanks for considering it! |
It doesn't. But the main use case for Supacode is:
If I go through W1, W3, W5, W4, it collapses to just the latest from A and the latest from B.
My suggestion was just to update this so worktrees are the main rows in the palette, not "projects". It'd return the same exact result for single-worktree scenarios like the one you're highlighting, but provide the full picture for every other case. And I said it specifically because I want to merge this, because I feel like it's the right abstraction, it just needs to expand to cover more use cases, instead of just a personal one.
I'm always open to evaluating and discussing stuff. If you open an issue you'll know for sure how I feel about it, instead of guessing 😅
I'm not just considering this. I'm 100% merging this cause you had a great idea with this 🙇♂️. But if you're not interested in expanding it, I can take it from here. |
|
Ah, I see what you're saying. I think. You want the worktree-switch to also be added to the stack, so the quick-switching includes flipping between them with the keyboard? Also being able to address the worktree directly somehow in the navigation palette by fuzzy-finding it on worktree-name combined with the project name? Are we on the same page? Because if we are, I think this makes a LOT of sense, and I'm happy to implement that. EDIT: I'll make a PR for the other feature when it lands with an accompanying video. |
|
Yep. Precisely that 💪 |
|
Just a drive by comment: looking forward to this feature! |
|
I would love to see a worktree navigation palette with the most recently accessed worktrees showing up first. I have a custom WezTerm setup which does this and it's the primary thing keeping me from switching to Supacode. Keep up the great work! |
|
Quick update — I've reworked this exactly as @sbertix suggested: worktrees are now the primary rows (MRU-ordered), so it covers the multi-worktree case rather than assuming one worktree per repo. @ryanb that also gives you the most-recently-accessed-worktree-first navigation you described 👍 I'm going to temporarily close this while I polish the ⌘P search ranking — right now a scattered fuzzy match can outrank a direct substring match, and recency isn't weighted strongly enough for a navigation surface. Once that feels right I'll reopen with the refreshed worktree-switcher version. Thanks for the patience. |
|
Thank you so much 🙇♂️ |
|
Thanks for the pull request. It doesn't meet the contribution policy yet, so I've labeled it
Nothing needs to be redone. A pull request left |
Don't worry about this, by the way. It can be bypassed 100% if you want to keep the commit history. Not an issue at all 🙇♂️ |
Adds a worktree switcher on ⌘P and moves the existing Command Palette to ⌘⇧P, aligning with VS Code (⌘⇧P = Command Palette; ⌘P = fast navigation). Both shortcuts remain user-rebindable via shortcut overrides. Reworked from the original project switcher per the PR supabitapp#370 review: Supacode's core use case is multi-worktree-per-repo, where a project-level switcher collapses the whole history down to one entry per repo and loses the worktree you actually had open. Worktrees are the primary rows instead. - ⌘P opens a fuzzy switcher of every worktree, ordered most-recently-used (RepositoriesFeature.State.worktreeMRU). The current worktree is rendered (so you see where you are) but flagged `isCurrentWorktree` so the default selection lands on the *previous* worktree — ⌘P then Enter is a Cmd+Tab-style toggle. Typing fuzzy-matches the combined `repo / worktree` title, so a query hits either the project name or the worktree name. - worktreeMRU is recorded on BOTH navigation paths — setSingleWorktreeSelection (hotkeys/palette) and reduceSelectionChangedEffect (sidebar clicks) — so it tracks however you navigate. Single-worktree repos behave exactly as the original project switcher did. - New PaletteMode (.commands / .worktreeSwitcher). The switcher emits `selectWorktree` directly (no project→worktree resolution step), so activation lands focus in the chosen terminal; cancelling refocuses the current terminal. Covered by CommandPaletteFeatureTests (switcher ordering/flagging, MRU sort, combined title, mode dispatch) and RepositoriesFeatureWorktreeMRUTests (MRU recording on both nav paths). Implemented with AI assistance (Claude Code).
…-beats-fuzzy ranking (#608) * Add ⌘P worktree switcher (VS Code-style) with worktree MRU Adds a worktree switcher on ⌘P and moves the existing Command Palette to ⌘⇧P, aligning with VS Code (⌘⇧P = Command Palette; ⌘P = fast navigation). Both shortcuts remain user-rebindable via shortcut overrides. Reworked from the original project switcher per the PR #370 review: Supacode's core use case is multi-worktree-per-repo, where a project-level switcher collapses the whole history down to one entry per repo and loses the worktree you actually had open. Worktrees are the primary rows instead. - ⌘P opens a fuzzy switcher of every worktree, ordered most-recently-used (RepositoriesFeature.State.worktreeMRU). The current worktree is rendered (so you see where you are) but flagged `isCurrentWorktree` so the default selection lands on the *previous* worktree — ⌘P then Enter is a Cmd+Tab-style toggle. Typing fuzzy-matches the combined `repo / worktree` title, so a query hits either the project name or the worktree name. - worktreeMRU is recorded on BOTH navigation paths — setSingleWorktreeSelection (hotkeys/palette) and reduceSelectionChangedEffect (sidebar clicks) — so it tracks however you navigate. Single-worktree repos behave exactly as the original project switcher did. - New PaletteMode (.commands / .worktreeSwitcher). The switcher emits `selectWorktree` directly (no project→worktree resolution step), so activation lands focus in the chosen terminal; cancelling refocuses the current terminal. Covered by CommandPaletteFeatureTests (switcher ordering/flagging, MRU sort, combined title, mode dispatch) and RepositoriesFeatureWorktreeMRUTests (MRU recording on both nav paths). Implemented with AI assistance (Claude Code). * Assert worktreeMRU in selection/history TestStore tests The worktree switcher records every concrete selection into the new `worktreeMRU` state on both nav paths (setSingleWorktreeSelection and reduceSelectionChangedEffect). TestStore checks state exhaustively, so the 27 selection/history tests that drive those paths now see an unasserted change. Assert the new MRU head in each — selecting worktree W prepends W; nil/archived selections leave it untouched (those tests already pass and are unchanged). Behavior change is intentional (the switcher's whole point), so the tests follow. * Polish ⌘P switcher: mode-aware placeholder + worktree/repo hierarchy Two bits of feedback from running the build: - The query placeholder was hardcoded "Search for actions or branches…" — stale now that ⌘P is worktree-only (actions live on ⌘⇧P). Make it mode-aware: "Go to worktree…" for the switcher, the actions wording for the command palette. - Switcher rows rendered one flat `repo / worktree` string at a single weight, so the worktree name was hard to pick out from the repo. Split them: worktree name is the title, repo is the secondary subtitle (VS Code "filename big, path small"). The fuzzy scorer matches title and subtitle, so a query still hits either name. Folder rows keep the repo name alone (no redundant subtitle). The ⌘⇧P command palette is unchanged (still the flat `repo / worktree` rows alongside actions). * Fix ⌘P search ranking: direct matches beat fuzzy, recency weighs in The shared CommandPaletteFuzzyScorer had two flaws the worktree switcher surfaced: - No tier between "prefix" and "scattered fuzzy", so a contiguous substring hit and a scattered subsequence hit landed in the same band — and the per-character position-0/separator bonuses could push a scattered match past a mid-word direct match. Added a contiguous-substring tier (title) and a direct (prefix/substring) tier for the subtitle, with every in-tier score clamped so it can never bleed into the tier above. Net: a direct match — including a clean repo (subtitle) hit — always outranks a scattered fuzzy one. This also undoes the regression from splitting switcher rows into worktree-title + repo-subtitle, where a fuzzy worktree hit buried an exact repo hit (subtitle matches previously scored in a no-threshold low band). - Recency (MRU) was a near-last tiebreaker, below match-spread. Lifted it above matchDistance so once match quality (the score tier) is equal, the most-recently-used row wins — the right default for a navigation surface. Pinned with directSubtitleMatchOutranksScatteredTitleMatch and contiguousSubstringOutranksScatteredSubsequence (both fail on the old scorer). * Skip Tuist auth on forks so fork CI can build The `setup-macos` Tuist auth step was gated to skip only cross-fork PRs, so an intra-fork PR (or a push to a fork's main) still tried `tuist auth login` and failed with `notAuthenticated` / "No projects linked to the repository" — the fork isn't linked in the Tuist dashboard. That blocked the whole build job before lint/build/test ran. Tuist auth is OIDC against the canonical upstream project and only buys the remote cache; `tuist generate` (and thus the build) works without it — the cross-fork-PR path already relies on this. Gate auth on the upstream repo so forks (and cross-fork PRs) skip it and can run the full build+test job. * Add fork-build CI to upload an installable Debug app artifact Local builds are impossible on this Mac (Zig/libSystem), and test.yml builds but never uploads the app, so a fork has no way to get a runnable build. Add a `make package-app` target (zips the Debug build via the same -showBuildSettings path resolution as run-app/install-dev-build) and a `fork-build.yml` workflow on feat/** | fix/** | exp/** that builds, packages, and uploads `supacode-<sha>`. No tests, no signing — just a downloadable .app.zip for local testing. * Finish the ⌘P worktree switcher: glass palette, sidebar-styled rows, MRU fixes Render the worktree switcher in the glass command-palette panel and keep ⌘⇧P listing actions only, since worktree navigation is the switcher's job. Style each switcher row like the sidebar: tint the worktree title and repo subtitle from the sidebar section and row colors, show a distinct wifi badge for a remote host, resolve folder rows to their custom name (falling back to the row's own name) with no subtitle, and draw the sidebar's leading glyph (branch or pull-request icon, folder, or missing) plus its CI check badge at the sidebar's resting opacity. A pull request whose head branch no longer matches the worktree falls back to the branch glyph. Promote a direct subtitle match above a scattered title match in the fuzzy scorer so an exact repo-name hit outranks a fuzzy worktree-name hit. Keep the worktree MRU correct: prune it in the shared removeWorktree primitive so every delete path drops the entry, keep transient pending-creation ids out of it, and cap it at the history-stack limit, so a reused path can't inherit a removed worktree's rank. Pass the active mode to the panel's keyboard-navigation filter so arrow keys and ⌘1-⌘5 drive the switcher rows, and force the command palette mode for Ghostty's toggle action so it never reopens the last-used surface. --------- Co-authored-by: Lars Thomas Denstad <ungedenstad@gmail.com> Co-authored-by: Stefano Bertagno <stefano@bertagno.com>
Closes #592
What
Adds a ⌘P worktree switcher (VS Code-style quick-open) and moves the existing Command Palette to ⌘⇧P.
Why this binding
Both are rebindable or disablable in Settings → Keyboard Shortcuts → General, so this only changes the defaults.
Behavior
Ranking — fuzzy search fixed
The fuzzy search that misbehaved in the earlier iteration of this PR is fixed. The shared scorer now guarantees direct beats fuzzy: a contiguous prefix/substring hit (including a clean repo-subtitle hit) always outranks a scattered subsequence hit — tiers are clamped so per-character bonuses can't bleed across a tier boundary. Recency (MRU) is lifted above match-spread so equal-quality matches resolve to the most-recently-used row. Pinned by
directSubtitleMatchOutranksScatteredTitleMatchandcontiguousSubstringOutranksScatteredSubsequence.Tests
CommandPaletteFeatureTests+RepositoriesFeatureWorktreeMRUTests— switcher ordering/flagging, defaultIndex seeding, sidebar-path MRU, and the ranking tiers. This PR's CI runsmake test.Note on the extra commits
This branch also carries two fork-only CI commits (
Skip Tuist auth on forks,Add fork-build CI…) that let the fork build an installable artifact where local builds are impossible. Happy to split them out if you'd rather keep this PR switcher-only.Disclosure
Implemented with AI assistance (Claude Code). Scorer ranking logic verified locally; the full build + test suite runs via this PR's CI.